package bluetooth;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;

/**
 * Performs a Bluetooth device scan. Default setup uses the BlueCove stack (BlueCove.jar in the
 * lib directory). This library can be replaced as required by the target installation. This is
 * only verified to work with a 1.5 JVM.
 *
 * @author Graeme Stevenson (graeme.stevenson@ucd.ie)
 */
public class BluetoothSpotter {

  /**
   * The logger for this class.
   */
  private static final Logger LOGGER = Logger
        .getLogger("spotter.bluetooth"); //$NON-NLS-1$

  /**
   * The bluetooth discovery my_discoveryAgent.
   */
  DiscoveryAgent my_discoveryAgent;

  /**
   * The my_discoveryListener called when a device is discovered.
   */
  DiscoveryListener my_discoveryListener;

  /**
   * List of devices spotted
   */
  ArrayList<DeviceDetails> devicesSpotted;

  /**
   * Is the scan complete?
   */
  public boolean scanInProgress;

  /**
   * Lock to ensure a single scan at a time.
   */
  public Lock my_scanLock;

  /**
   * Sets up a new spotter.
   */
  public BluetoothSpotter() throws BluetoothStateException {
     my_scanLock = new ReentrantLock();
     my_discoveryAgent = LocalDevice.getLocalDevice().getDiscoveryAgent();
     my_discoveryListener = new BluetoothListener();
     devicesSpotted = new ArrayList<DeviceDetails>();
     scanInProgress = false;
  }

  /**
   * Listener class that is notified when a device is discovered.
   */
  class BluetoothListener implements DiscoveryListener {

     /**
      * Called when a device is discovered. Stores the mac address and the human readable
      * name.
      */
     public void deviceDiscovered(RemoteDevice a_remoteDevice, DeviceClass the_deviceClass) {
        // Get the device ID corresponding to the MAC address

        try {
           String remote_address = a_remoteDevice.getBluetoothAddress();
           // remove colon seperators from the bluetooth mac address string
           // for compatability across all platforms
           String parts[] = remote_address.split(":");
           String remote_address_normalised = "";
           for (int i = 0; i < parts.length; i++) {
              remote_address_normalised += parts[i];
           }
           // set characters to upper case - again for compatability....
           remote_address_normalised = remote_address_normalised.toUpperCase();
           String deviceName = a_remoteDevice.getFriendlyName(false);
           DeviceDetails newDevice = new DeviceDetails(remote_address_normalised, deviceName);
           devicesSpotted.add(newDevice);
        } catch (Exception e) {
           LOGGER.warning(e.getMessage());
        }
     }

     /**
      * Called when a scan ends.
      */
     public void inquiryCompleted(int the_discoveryType) {
        scanInProgress = false;
     }

     /**
      * Unused.
      */
     public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {

     }

     /**
      * Unused.
      */
     public void serviceSearchCompleted(int transID, int respCode) {

     }
  }

  /**
   * Performs a scan, waits for it to complete, and then returns the details of deiscovered
   * devices
   *
   * @return a list containing the details of spotted devices.
   */
  public synchronized List<DeviceDetails> performScan() {
     List<DeviceDetails> result = new ArrayList<DeviceDetails>();
    
     try {
        my_scanLock.lock();
        scanInProgress = true;
        my_discoveryAgent.startInquiry(DiscoveryAgent.GIAC, my_discoveryListener);

        while (scanInProgress) {
           wait(100);
        }
        result = (List<DeviceDetails>) devicesSpotted.clone();
     } catch (InterruptedException e) {
        LOGGER.severe(e.getMessage());
     } catch (BluetoothStateException e) {
        LOGGER.severe(e.getMessage());
     } finally {
        devicesSpotted.clear();
        my_scanLock.unlock();
     }
     return result;
  }

  public static void main(String[] args) {
     try {
        // Create a new Bluetooth Spotter
        BluetoothSpotter spotter = new BluetoothSpotter();
        // Perform the scan. This could take up to 30 seconds...
        List<DeviceDetails> devices = spotter.performScan();
        // Iterate through all detected devices and print details to the console
        for (DeviceDetails device : devices) {
           System.out.println(device.getDeviceAddress() + " - " + device.getDeviceName());
        }
     } catch (BluetoothStateException an_exception) {
        an_exception.printStackTrace();
     }

  }

}